package com.vlyman.shiro.config;

import com.vlyman.builder.QueryWrapperBuilder;
import com.vlyman.consts.UriConsts;
import com.vlyman.rtn.SystemRtn;
import com.vlyman.shiro.cache.VlymanShiroCacheManager;
import com.vlyman.shiro.entity.SysPermissionFilter;
import com.vlyman.shiro.jwt.JwtFilter;
import com.vlyman.shiro.realm.VlymanRealm;
import com.vlyman.shiro.service.SysPermissionFilterService;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.mgt.DefaultSessionStorageEvaluator;
import org.apache.shiro.mgt.DefaultSubjectDAO;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.servlet.Filter;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * 类描述：
 *  shiro配置
 *  Apache Shiro核心通过Filter来实现，通过URL规则来进行过滤和权限校验
 * @author chenjs
 * @date 2018/12/17 14:58
 **/
@Slf4j
@Configuration
public class ShiroConfig {

    @Autowired(required = false)
    private SysPermissionFilterService sysPermissionFilterServiceImpl;

    /**
     * 方法描述：
     *   配置Shiro拦截器工厂类
     *  @author chenjs
     *  @param securityManager
     *  @return ShiroFilterFactoryBean
     *  @date 2018/12/19 12:45
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        //必须设置 SecurityManager
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        // 添加自己的过滤器取名为jwt
        Map<String, Filter> filterMap = new HashMap<>(4);
        filterMap.put("jwt", new JwtFilter());
        shiroFilterFactoryBean.setFilters(filterMap);
        //setLoginUrl 如果不设置值，默认会自动寻找Web工程根目录下的"/login.jsp"页面 或 "/login" 映射
        shiroFilterFactoryBean.setLoginUrl(UriConsts.SYSBASE_ERROR_PREFIX+"/system/"+SystemRtn.NOT_LOGIN.getCode());
        //设置无权限时跳转的 url;
        shiroFilterFactoryBean.setUnauthorizedUrl(UriConsts.SYSBASE_ERROR_PREFIX+"/system/"+SystemRtn.AUTH_MISSING.getCode());
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        List<SysPermissionFilter> filterList = sysPermissionFilterServiceImpl.list(
                QueryWrapperBuilder.init(SysPermissionFilter.class)
                        .orderByAsc(SysPermissionFilter.SORT)
        );
        filterList.forEach(item -> filterChainDefinitionMap.put(
                item.getUri(), item.getPermission()
        ));
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        log.info("Shiro拦截器工厂类注入成功");
        return shiroFilterFactoryBean;
    }

    /**
     * 方法描述：
     *   注入securityManager
     *  @author chenjs
     *  @return SecurityManager
     *  @date 2018/12/19 16:24
     */
    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // 设置自定义realm
        securityManager.setRealm(createVlymanRealm());
        // 针对jwt自定义缓存实现 使用redis
        securityManager.setCacheManager(cacheManager());
        // 禁用原有shiro的session功能，用jwt替换
        securityManager.setSubjectDAO(createSubjectDAO());
        return securityManager;
    }

    /**
     * 方法描述：
     *   自定义身份认证realm
     *   必须写这个类，并加上@Bean 注解，目的是注入CustomRealm
     *   否则会影响CustomRealm类中其他类的依赖注入
     *  @author chenjs
     *  @return CustomRealm
     *  @date 2018/12/19 16:29
     */
    @Bean
    public VlymanRealm createVlymanRealm() {
        return new VlymanRealm();
    }


    /**
     * 方法描述：
     *   Shiro缓存管理器
     *  @author chenjs
     *  @return VlymanShiroCacheManager jwt缓存管理
     *  @date 2019/1/10 15:21
     */
    public VlymanShiroCacheManager cacheManager() {
        return new VlymanShiroCacheManager();
    }

    /**
     * 方法描述：
     *   Subject的DAO操作
     *  @author chenjs
     *  @return DefaultSubjectDAO
     *  @date 2019/1/10 15:25
     */
    public DefaultSubjectDAO createSubjectDAO() {
        DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
        DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator();
        // 关闭Shiro自带的session
        defaultSessionStorageEvaluator.setSessionStorageEnabled(false);
        subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator);
        return subjectDAO;
    }
}
